#include "Crawl.hpp"
#include "String.hpp"

#include <map>
#include <cassert>
#include <iostream>

using namespace std;

pthread_mutex_t  gMutex;
pthread_t*       gThreadArray;

Crawl::Crawl(const string& crPath, int threadNum)
    : mPath(crPath), mThreadNum(threadNum), mCurrentTWFile(0)
{
    //     this->mThreadNum = threadNum;
    gThreadArray = new pthread_t[this->mThreadNum];
    pthread_mutex_init(&gMutex, NULL);
}

Crawl::~Crawl()
{
    delete [] gThreadArray;
    pthread_mutex_destroy(&gMutex);
}

// void
// Crawl::ImportUrl(const string& crUrl)
// {
//     pthread_mutex_lock(&gMutex);
//     this->mUrlQueue.push(crUrl);
//     pthread_mutex_unlock(&gMutex);
// }

int
Crawl::ImportTaskQueue(const vector<queue<Url> >& crTaskQueue)
{
    assert((int)crTaskQueue.size() <= mThreadNum);
    mTaskQueue = crTaskQueue;
    return (int)crTaskQueue.size();
}

// 这个函数可以使用static的成员函数，在头文件中声明
void *
Crawl::StartThread(void* arg)
{
//     Para* para = (Para* )(arg);
//     cout << "\t#" << para->thread_idx << endl;
//     ((Crawl* )para->pt) ->fetch(para->thread_idx);
    (static_cast<Crawl *>(arg))->fetch();
    return ((void *)0);
}

int
Crawl::StartCrawling()
{
    mUrlList.clear();
    mCurrentQueue = 0;
    string cur_tw_file_name = mPath + String::Type2String<int>(mCurrentTWFile) + ".tw";
    mFileStream.open(cur_tw_file_name.c_str());
    int err = 0;
    for (unsigned i=0; i<this->mTaskQueue.size(); ++i) {
        // XXXX
        //         Para* para = new Para;
        //         para->pt = this;
        //         para->thread_idx = i;
        err = pthread_create(&gThreadArray[i], NULL, StartThread, this);
        if (err == -1) {
            // should log this
            break;
        }
        //         delete para;
    }

    for (unsigned i=0; i<this->mTaskQueue.size(); ++i) {
        pthread_join(gThreadArray[i], NULL);
    }
    mFileStream.close();
    ++mCurrentTWFile;
    return err;
}

void
Crawl::fetch()
{
    /// @todo 添加天网格式支持
    pthread_mutex_lock(&gMutex);
    queue<Url>& queue_local = mTaskQueue[mCurrentQueue];
    ++mCurrentQueue;
    pthread_mutex_unlock(&gMutex);

//     char*     file_name = new char[11];
//     pthread_t pid       = pthread_self();

//     sprintf(file_name, "%u", (unsigned)pid);
//     ofstream fout(file_name);

    Http http_fetch;

    bool is_url_queen_empty = false;

    while (!is_url_queen_empty) {
        Url url_local;
        if (!queue_local.empty()) {
            url_local = queue_local.front();
            queue_local.pop();
        } else {
            break;
        }
//         pthread_mutex_lock(&gMutex);
//         string current_url;
//         if (!this->mUrlQueue.empty()) {
//             current_url = this->mUrlQueue.front();
//             this->mUrlQueue.pop();
//         } else {
//             is_url_queen_empty = true;
//         }

//         pthread_mutex_unlock(&gMutex);

//         Url url_local;
//         url_local.ParseUrl(current_url);

        Page tmp_page;
        /// Store the result to TianwangFile Format
        if (http_fetch.FetchUrl(url_local, tmp_page)) {
            pthread_mutex_lock(&gMutex);
            tmp_page.SetOrginUrl(url_local.mOrginUrl);
            tmp_page.ParseHyperLinks();
            vector<string> url_list = tmp_page.GetUrlFromPage();
            /// @todo use insert instead of this
            for (unsigned i=0; i<url_list.size(); ++i) {
                mUrlList.push_back(url_list[i]);
            }
            mTWFile.Store(mFileStream, url_local, tmp_page);
            cout << "Fetch Url : " << url_local.mOrginUrl << endl;
            pthread_mutex_unlock(&gMutex);
        }
    }
//     delete [] file_name;
}

vector<string>
Crawl::GetUrlList()
{
    return mUrlList;
}
